import React, { ReactNode } from 'react';
import cx from 'classnames';
import { Moment } from 'moment';

import DateConstants from './DateConstants';
import { getTitleString, getTodayTime } from '../util';

function isSameDay(one, two) {
  return one && two && one.isSame(two, 'day');
}

function beforeCurrentMonthYear(current, today) {
  if (current.year() < today.year()) {
    return 1;
  }
  return current.year() === today.year() && current.month() < today.month();
}

function afterCurrentMonthYear(current, today) {
  if (current.year() > today.year()) {
    return 1;
  }
  return current.year() === today.year() && current.month() > today.month();
}

function getIdFromDate(date) {
  return `rc-calendar-${date.year()}-${date.month()}-${date.date()}`;
}

export interface DateTBodyProps {
  contentRender?: (current: Moment, value: Moment) => ReactNode;
  value?: Moment;
  showWeekNumber?: boolean;
  dateRender?: (current: Moment, value: Moment) => ReactNode;
  selectedValue?: Moment | Moment[];
  prefixCls?: string;
  disabledDate?: (next: Moment, value: Moment) => boolean;
  hoverValue?: Moment[];
  // onSelect?: (current: Moment | null, value: Moment) => void;
  onSelect?: any;
  onDayHover?: (current: Moment | null, value: Moment) => void;
}

const DateTBody: React.FC<DateTBodyProps> = props => {
  const {
    contentRender,
    prefixCls,
    selectedValue,
    value,
    showWeekNumber,
    dateRender,
    disabledDate,
    hoverValue,
  } = props;

  // very much className
  const cellClass = `${prefixCls}-cell`;
  const weekNumberCellClass = `${prefixCls}-week-number-cell`;
  const dateClass = `${prefixCls}-date`;
  const todayClass = `${prefixCls}-today`;
  const selectedClass = `${prefixCls}-selected-day`;
  const selectedDateClass = `${prefixCls}-selected-date`; // do not move with mouse operation
  const selectedStartDateClass = `${prefixCls}-selected-start-date`;
  const selectedEndDateClass = `${prefixCls}-selected-end-date`;
  const inRangeClass = `${prefixCls}-in-range-cell`;
  const lastMonthDayClass = `${prefixCls}-last-month-cell`;
  const nextMonthDayClass = `${prefixCls}-next-month-btn-day`;
  const disabledClass = `${prefixCls}-disabled-cell`;
  const firstDisableClass = `${prefixCls}-disabled-cell-first-of-row`;
  const lastDisableClass = `${prefixCls}-disabled-cell-last-of-row`;
  const lastDayOfMonthClass = `${prefixCls}-last-day-of-month`;

  // Why?
  let iIndex;
  let jIndex;
  let current: Moment;
  const today = getTodayTime(value);
  const dateTable: Moment[] = [];

  const month1 = value.clone();
  month1.date(1);
  const day = month1.day();
  const lastMonthDiffDay = (day + 7 - value.localeData().firstDayOfWeek()) % 7;
  // calculate last month
  const lastMonth1 = month1.clone();
  lastMonth1.add(0 - lastMonthDiffDay, 'days');
  let passed = 0;

  for (iIndex = 0; iIndex < DateConstants.DATE_ROW_COUNT; iIndex += 1) {
    for (jIndex = 0; jIndex < DateConstants.DATE_COL_COUNT; jIndex += 1) {
      current = lastMonth1;
      if (passed) {
        current = current.clone();
        current.add(passed, 'days');
      }
      dateTable.push(current);
      passed += 1;
    }
  }
  const tableHtml = [];
  passed = 0;

  for (iIndex = 0; iIndex < DateConstants.DATE_ROW_COUNT; iIndex += 1) {
    let isCurrentWeek;
    let weekNumberCell;
    let isActiveWeek = false;
    const dateCells = [];
    if (showWeekNumber) {
      weekNumberCell = (
        <td key={dateTable[passed].week()} role="gridcell" className={weekNumberCellClass}>
          {dateTable[passed].week()}
        </td>
      );
    }
    for (jIndex = 0; jIndex < DateConstants.DATE_COL_COUNT; jIndex += 1) {
      let next = null;
      let last = null;
      current = dateTable[passed];
      if (jIndex < DateConstants.DATE_COL_COUNT - 1) {
        next = dateTable[passed + 1];
      }
      if (jIndex > 0) {
        last = dateTable[passed - 1];
      }
      let cls = cellClass;
      let disabled = false;
      let selected = false;

      if (isSameDay(current, today)) {
        cls += ` ${todayClass}`;
        isCurrentWeek = true;
      }

      const isBeforeCurrentMonthYear = beforeCurrentMonthYear(current, value);
      const isAfterCurrentMonthYear = afterCurrentMonthYear(current, value);

      if (selectedValue && Array.isArray(selectedValue)) {
        const rangeValue = hoverValue.length ? hoverValue : selectedValue;
        if (!isBeforeCurrentMonthYear && !isAfterCurrentMonthYear) {
          const startValue = rangeValue[0];
          const endValue = rangeValue[1];
          if (startValue) {
            if (isSameDay(current, startValue)) {
              selected = true;
              isActiveWeek = true;
              cls += ` ${selectedStartDateClass}`;
            }
          }
          if (startValue || endValue) {
            if (isSameDay(current, endValue)) {
              selected = true;
              isActiveWeek = true;
              cls += ` ${selectedEndDateClass}`;
            } else if (
              (startValue === null || startValue === undefined) &&
              current.isBefore(endValue, 'day')
            ) {
              cls += ` ${inRangeClass}`;
            } else if (
              (endValue === null || endValue === undefined) &&
              current.isAfter(startValue, 'day')
            ) {
              cls += ` ${inRangeClass}`;
            } else if (current.isAfter(startValue, 'day') && current.isBefore(endValue, 'day')) {
              cls += ` ${inRangeClass}`;
            }
          }
        }
      } else if (isSameDay(current, value)) {
        // keyboard change value, highlight works
        selected = true;
        isActiveWeek = true;
      }

      if (isSameDay(current, selectedValue)) {
        cls += ` ${selectedDateClass}`;
      }

      if (isBeforeCurrentMonthYear) {
        cls += ` ${lastMonthDayClass}`;
      }

      if (isAfterCurrentMonthYear) {
        cls += ` ${nextMonthDayClass}`;
      }

      if (
        current
          .clone()
          .endOf('month')
          .date() === current.date()
      ) {
        cls += ` ${lastDayOfMonthClass}`;
      }

      if (disabledDate) {
        if (disabledDate(current, value)) {
          disabled = true;

          if (!last || !disabledDate(last, value)) {
            cls += ` ${firstDisableClass}`;
          }

          if (!next || !disabledDate(next, value)) {
            cls += ` ${lastDisableClass}`;
          }
        }
      }

      if (selected) {
        cls += ` ${selectedClass}`;
      }

      if (disabled) {
        cls += ` ${disabledClass}`;
      }

      let dateHtml;
      if (dateRender) {
        dateHtml = dateRender(current, value);
      } else {
        const content = contentRender ? contentRender(current, value) : current.date();
        dateHtml = (
          <div
            key={getIdFromDate(current)}
            className={dateClass}
            aria-selected={selected}
            aria-disabled={disabled}
          >
            {content}
          </div>
        );
      }

      dateCells.push(
        <td
          key={passed}
          onClick={disabled ? undefined : props.onSelect.bind(null, current)}
          onMouseEnter={
            disabled
              ? undefined
              : (props.onDayHover && props.onDayHover.bind(null, current)) || undefined
          }
          role="gridcell"
          title={getTitleString(current)}
          className={cls}
        >
          {dateHtml}
        </td>,
      );

      passed += 1;
    }

    tableHtml.push(
      <tr
        key={iIndex}
        role="row"
        className={cx({
          [`${prefixCls}-current-week`]: isCurrentWeek,
          [`${prefixCls}-active-week`]: isActiveWeek,
        })}
      >
        {weekNumberCell}
        {dateCells}
      </tr>,
    );
  }
  // ??? what happened
  return <tbody className={`${prefixCls}-tbody`}>{tableHtml}</tbody>;
};

export default DateTBody;
